exception Requete_en_cours
exception Pas_de_requete
type reponse =
  | Accepte
  | Refuse
  | Erreur
type 'carte joueur =
    {
      mutable connexion: Connexion.joueur;
      mutable numero: int option;
      encheres: int option array;
      mutable preneur: int option;
      mutable appel: 'carte option;
      mutable ecart: 'carte list option; 
      (* vaut None si ce n'est pas encore décidé *)
      mutable prise_demandee: int option;
      mutable appel_demande: 'carte option;
      mutable ecart_demande: 'carte list option;
      mutable chaine_reponse: (reponse -> unit) list;
    }
let est_connecte j = Connexion.est_connecte j.connexion
let string_of_joueur j = Connexion.string_of_joueur j.connexion
let joueurs_disponibles j = Connexion.joueurs_disponibles j.connexion
let mes_adversaires j = Connexion.mes_adversaires j.connexion
let en_jeu j = Connexion.joue j.connexion
let mon_numero j = j.numero
let enchere_de j i = j.encheres.(i)
let preneur j = j.preneur
let carte_appelee j = j.appel
let ecart_connu j = j.ecart
let requete_en_cours j =
  if Connexion.requete_en_cours j.connexion then true
  else match j.prise_demandee, j.appel_demande, 
	     j.ecart_demande with
       | None, None, None -> false
       | Some x, _, _ -> 
	  (* let () =  *)
	  (*   Printf.printf  *)
	  (*     "Attention : %s est en train de priser (%d).\n" *)
	  (*     (string_of_joueur j) x *)
	  (* in *)
	  true
       | _, Some c, _ ->
	  (* let () =  *)
	  (*   Printf.printf  *)
	  (*     "Attention : %s est en train d'appeler une carte.\n" *)
	  (*     (string_of_joueur j) *)
	  (* in *)
	  true
       | _, _, Some [a ; b ; c] ->
	  (* let () =  *)
	  (*   Printf.printf  *)
	  (*     "Attention : %s est en train de faire un écart.\n" *)
	  (*     (string_of_joueur j)  *)
	  (* in *)
	  true
       | _ -> true
let doit_priser j =
  match j.numero with
  | None -> false
  | Some k ->
     let ont_prise = Array.map (function | Some _ -> true | _ -> false)
			       j.encheres in
     let joueurs_precedents = Array.sub ont_prise 0 k in
     let precedents_ont_prise = 
       Array.fold_left (&&) true joueurs_precedents in
     let j_ai_prise = ont_prise.(k)
		      || match j.prise_demandee with
			 | None -> false | _ -> true in
     precedents_ont_prise && not (j_ai_prise)
let enchere_maximale j =
  (* Retourne le couple (preneur temporaire, enchère *)
  let contrats = Array.map (function Some x -> x | _ -> (-1))
			      j.encheres in
  let indexes = Array.mapi (fun i x -> (i, x)) contrats in
  let comparer (i_max, max) (i, x) =
    if x > max then (i, x)
    else (i_max, max)
  in 
  Array.fold_left (comparer) indexes.(0) indexes
let doit_appeler j =
  match j.preneur, j.appel, j.appel_demande, j.numero with
  | (Some k, None, None, Some l) when k = l ->
     (* C'est moi le preneur, pas de carte appelée, pas de carte en attente *)
     true
  | _ -> false
let doit_ecarter j =
  match j.preneur, j.ecart, j.ecart_demande, j.numero with
  | (Some k, None, None, Some l) when k = l -> true
  | _ -> false

let make () =
  {
    connexion = Connexion.make ();
    numero = None;
    encheres = Array.make 5 None;
    preneur = None;
    appel = None;
    ecart = None;
    prise_demandee = None;
    appel_demande = None;
    ecart_demande = None;
    chaine_reponse = [];
  }

let traiter_reponse joueur f =
  if Connexion.requete_en_cours joueur.connexion then
    Connexion.traiter_reponse 
      joueur.connexion 
      (function Connexion.Accepte -> f Accepte
	      | Connexion.Refuse -> f Refuse
	      | Connexion.Erreur -> f Erreur)
  else if requete_en_cours joueur then
    joueur.chaine_reponse <- (f) :: joueur.chaine_reponse
  else raise Pas_de_requete
let repondre_aux joueur rep =
  if Connexion.requete_en_cours joueur.connexion then
    Connexion.repondre_aux 
      joueur.connexion 
      begin match rep with
	    | Accepte -> Connexion.Accepte
	    | Refuse -> Connexion.Refuse
	    | Erreur -> Connexion.Erreur end
  else
    begin
      List.iter (fun f -> f rep)
		(List.rev joueur.chaine_reponse) ;
      joueur.chaine_reponse <- [];
      joueur.prise_demandee <- None;
      joueur.appel_demande <- None;
      joueur.ecart_demande <- None;
    end
let requete_refusee joueur =
  repondre_aux joueur Refuse
let requete_acceptee joueur =
  repondre_aux joueur Accepte
let assimiler_requete_deconnecter joueur =
  Connexion.assimiler_requete_deconnecter joueur.connexion;
  repondre_aux joueur Erreur
let assimiler_requete_identifier joueur nom =
  if requete_en_cours joueur then
    raise Requete_en_cours
  else
    Connexion.assimiler_requete_identifier joueur.connexion nom
let assimiler_requete_inviter joueur noms =
  if requete_en_cours joueur then
    raise Requete_en_cours
  else
    Connexion.assimiler_requete_inviter joueur.connexion noms
let assimiler_requete_prise joueur prise =
  if requete_en_cours joueur then
    raise Requete_en_cours
  else
    joueur.prise_demandee <- Some prise
let assimiler_requete_appel joueur carte =
  if requete_en_cours joueur then
    raise Requete_en_cours
  else 
    joueur.appel_demande <- Some carte
let assimiler_requete_ecart joueur cartes =
  if requete_en_cours joueur then
    raise Requete_en_cours
  else
    let () = joueur.ecart_demande <- Some cartes in
    (* Ajout des cartes si on accepte mon écart *)
    traiter_reponse joueur (function Accepte -> 
				     joueur.ecart <- Some cartes
				   | _ -> ())
let assimiler_information_erreur joueur =
  repondre_aux joueur Refuse
let assimiler_information_connexion joueur =
  Connexion.assimiler_information_connexion joueur.connexion
let assimiler_information_deconnexion joueur =
  Connexion.assimiler_information_deconnexion joueur.connexion;
  repondre_aux joueur Erreur
let assimiler_information_entree joueur =
  Connexion.assimiler_information_entree joueur.connexion
let assimiler_information_sortie joueur =
  Connexion.assimiler_information_sortie joueur.connexion
let assimiler_information_noms joueur noms =
  Connexion.assimiler_information_noms joueur.connexion noms ;
  joueur.numero <- None;
  for i = 0 to -1 + Array.length joueur.encheres do
    joueur.encheres.(i) <- None
  done ;
  joueur.appel <- None;
  joueur.ecart <- None;
  joueur.preneur <- None;
  (* Printf.printf "Écart réinitialisé.\n" ; *)
  (* On clôt les requêtes *)
  repondre_aux joueur Erreur
let assimiler_information_numero joueur n =
  joueur.numero <- Some n
let assimiler_information_contrat joueur x =
  let rec placer_en i =
    if i < Array.length joueur.encheres then
      match joueur.encheres.(i) with
      | None -> 
	 let () = 
	   match joueur.numero with
	   | None -> failwith "Ne peut arriver."
	   | Some n when n = i ->
	      repondre_aux joueur Accepte
	   | _ -> ()
	 in
	 joueur.encheres.(i) <- Some x
      | _ -> (* Il a déjà fait son contrat *)
	 placer_en (i + 1)
    else failwith "Tout le monde a déjà prisé !"
  in
  placer_en 0
let assimiler_information_appel joueur =
  (* Je dois appeler ! *)
  match joueur.numero with
  | None ->
     failwith 
       ("Erreur : le serveur veut que j'appelle une carte, "
	^"mais je ne sais même pas quel est mon numéro de tour !")
  | Some num ->
     joueur.preneur <- Some num
let assimiler_information_contrat_final string_of_carte
					joueur preneur contrat appel =
  joueur.preneur <- Some preneur;
  let () =
    (* Si je suis le dernier, ma prise a été acceptée. *)
    match joueur.numero with
    | Some 4 -> requete_acceptee joueur
    | _ -> ()
  in
  let () = 
    match joueur.encheres.(preneur) with
    | Some x when x = contrat ->
       begin
	 match joueur.appel with
	 | Some x when x = appel ->
	    (* Je suis le preneur et le serveur a bien compris. *)
	    (* Rien à faire, je sais déjà tout. *)
	    ()
	 | None ->
	    joueur.appel <- Some appel
	 | Some y ->
	    failwith
	      ("Le serveur n'a pas bien compris la carte que je voulais "
	       ^" appeler : c'était "
	       ^(string_of_carte y)
	       ^" et non "^(string_of_carte appel)^".")
       end
    | None ->
       let () = joueur.preneur <- Some preneur in
       joueur.appel <- Some appel
    | Some contrat_selon_moi ->
       failwith 
	 ("Je ne suis pas d'accord avec le serveur : pour moi, "
	  ^(mes_adversaires joueur).(preneur)
	  ^" a fait l'enchère "
	  ^(string_of_int contrat_selon_moi)
	  ^" et pas "^(string_of_int contrat)^".")
  in
  (* On accepte la requête d'appel *)
  match joueur.appel_demande, joueur.preneur, joueur.numero with
  | Some _, Some moi1, Some moi2 when moi1 = moi2 ->
     (* Transaction acceptée *)
     requete_acceptee joueur
  | _ -> ()
let assimiler_information_atout_au_chien joueur atouts =
  match joueur.ecart with
  | None -> joueur.ecart <- Some atouts
  | _ -> () (* Je connais mon écart *)
let assimiler_information_jeu joueur =
  let () =
    (* Si je suis le preneur, j'accepte mon écart éventuel. *)
    match joueur.ecart_demande with
    | None -> ()
    | _ -> match joueur.numero, joueur.preneur with
	   | Some n1, Some n2 when n1 = n2 -> requete_acceptee joueur
	   | _ -> ()
  in
  match joueur.ecart with
  | None -> joueur.ecart <- Some []
  | _ -> 
     () (* Je mets quelque chose sauf si je connais quelque chose de l'écart *)
